package com.bpvse;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.util.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Random;



//
//        连接Redis数据库。
//        从Redis中获取前一个版本的数据，并生成一个新的随机版本。
//        对每个关键字，计算相应的状态、索引和证明，并将它们存储到Redis中。
//        将新的版本号和对应的数据存储到Redis中。
//        将新版本的数据复制到输出参数中，并返回新的版本号。

@Service
public class UpdateService {
    private static final int HASH_LEN = 32; // 假设哈希长度为32字节
    private static final String strEOF = "EOF";
    private static final int MAX_BLOCK_SIZE = 16;  //最大块大小
    private static final int KEY_LEN = 32; // 密钥的长度，你需要根据实际情况调整

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public int update(byte[] stl, byte[] ckey, int l, byte[] W, int[] DB, int wL, byte[] sIndex) throws Exception{
        byte[] sDB = new byte[HASH_LEN + 4];
        byte[] v1 = new byte[HASH_LEN];
        byte[] v2 = new byte[HASH_LEN];
        byte[] st1 = new byte[HASH_LEN];
        byte[] st1Message = new byte[HASH_LEN];
        byte[] st2Message = new byte[HASH_LEN];
        byte[] st2 = new byte[HASH_LEN];
        byte[] Pa1 = new byte[HASH_LEN];
        byte[] Pv1 = new byte[HASH_LEN];
        byte[] Pa2 = new byte[HASH_LEN];
        byte[] Pv2 = new byte[HASH_LEN];
        byte[] Sv1 = new byte[HASH_LEN + 4];
        byte[] Sa1 = new byte[HASH_LEN];
        byte[] sk = new byte[HASH_LEN];
        byte[] EI = new byte[HASH_LEN];
        byte[] IH = new byte[HASH_LEN];
        byte[] Ia = new byte[HASH_LEN];
        byte[] Iv = new byte[HASH_LEN];
        byte[] hd = new byte[HASH_LEN];
        byte[] sI = new byte[288]; //320-32

        Arrays.fill(IH, (byte) 0);
        //将版本号+1
        l = l + 1;

        // 从Redis中获取前一个版本的数据v1
        String prevVersion = stringRedisTemplate.opsForValue().get(String.valueOf(l - 1));
        System.out.println("prevVersion的内容是："+prevVersion);
        if (prevVersion != null) {
            v1 = Base64.getDecoder().decode(prevVersion);
            System.out.println("v1的内容是"+Arrays.toString(v1));
            System.out.println("v1的加密内容是:"+Base64.getEncoder().encodeToString(v1));

        } else {
            v1 = strEOF.getBytes(); //将当前版本的前一版本置空
        }

        // 使用randomStrUnit生成一个随机字符串v2（生成一个新的随机版本f）
        randomStrUnit.randomStr(v2, HASH_LEN);

        // 循环处理每个关键字wi
        for (int i = 0; i < wL; i++) {
            W[0] = (byte) i;
            // 如果v1不等于EOF，使用F1Unit计算st1；否则将st1设置为EOF，并将Pa1和EOF存储到Redis
            if (!Arrays.equals(v1, strEOF.getBytes())) {   //前面存在对应关键字的版本
                System.out.println("w的内容是"+W);
                F1Unit.F1(st1, W, DataUnit.WORD_LEN, v1, ckey, 1);  //计算出前一个版本的状态，W[0]的改变决定了每个关键字对应的st是不同的，导致pv2是不同的，由于最后的W[0]是最新版本对应的最后一个关键字，所以陷门找到的也就是最新版本的最后一个关键字所对应的状态
                System.arraycopy(st1, 0, st1Message, 0, st1.length);  //直接通过v1获得了st1
                System.out.println("st1的内容：" + Arrays.toString(st1));
                System.out.println("st1的加密内容是:" + bytesToHexUnit.byteTo(st1));
                hUnit.h(Pa1, st1Message, HASH_LEN, (byte) 0); //计算st1不为空时，pa1的值
                System.out.println("Pa1的内容是多少" + Arrays.toString(Pa1));
                System.out.println("Pa1的加密内容是:" + Base64.getEncoder().encodeToString(Pa1));

            } else {  //第一次更新，前面没有对应关键字的版本
                st1 = strEOF.getBytes();    //前一版本的状态为null
                //System.out.println(st1.length);
                System.arraycopy(st1, 0, st1Message, 0, st1.length);
                hUnit.h(Pa1, st1Message, HASH_LEN, (byte) 0);  //计算st1为空时，pa1的值
                stringRedisTemplate.opsForValue().set(Base64.getEncoder().encodeToString(Pa1), Base64.getEncoder().encodeToString(strEOF.getBytes()));
                System.out.println("第一次开始更新时Pa1的内容是:" + Arrays.toString(Pa1));
                System.out.println("第一次开始更新时Pa1的加密内容是:" + Base64.getEncoder().encodeToString(Pa1));
            }

            // 从Redis中获取Pa1对应的值Pv1
            String pv1Str = stringRedisTemplate.opsForValue().get(Base64.getEncoder().encodeToString(Pa1));
            System.out.println("pv1Str的值是多少:" + pv1Str);
            if (pv1Str != null) {
                Pv1 = Base64.getDecoder().decode(pv1Str);
                System.out.println("Pv1的值是多少:" + Arrays.toString(Pv1));
                System.out.println("Pv1的加密内容是:" + Base64.getEncoder().encodeToString(Pv1));
            }



            /*
              开始计算v2版本
             */

            // 使用F1Unit计算st2
            //通过st2计算Pa2,将，st2和Pa2联系起来，然后通过Pa2指向Pv2,这样就可以串起来。
            System.out.println("w的内容是"+W);
            F1Unit.F1(st2, W, DataUnit.WORD_LEN, v2, ckey, 1);
            System.arraycopy(st2, 0, st2Message, 0, st2.length);
            System.out.println("st2的内容：" + Arrays.toString(st2));
            System.out.println("st2的加密内容是:" + Base64.getEncoder().encodeToString(st2));
            hUnit.h(Pa2, st2Message, HASH_LEN, (byte) 0); //计算st1不为空时，pa1的值
            System.out.println("Pa2的内容是多少" + Arrays.toString(Pa2));
            System.out.println("Pa2的加密内容是:" + Base64.getEncoder().encodeToString(Pa2));

            // 使用hUnit计算Sa1
            hUnit.h(Sa1, st2, HASH_LEN, (byte) 1);
            System.out.println("Sa1的内容：" + Arrays.toString(Sa1));
            System.out.println("Sa1的加密内容是:" + Base64.getEncoder().encodeToString(Sa1));
            // 使用h2Unit计算Sv1
            h2Unit.h2(Sv1, st2, HASH_LEN);
            // 使用int2charUnit将DB[0]转换为字符数组sDB
            int2charUnit.int2char(sDB, DB[0]);

            if (st1Message.length >= HASH_LEN && sDB.length >= 4 + HASH_LEN) {
                // 将st1的前HASH_LEN个字节复制到sDB的第4个字节开始的位置
                System.arraycopy(st1Message, 0, sDB, 4, HASH_LEN);
            } else {
                System.out.println("报错，数组越界");
            }

            // 使用strXorUnit计算Sv1和sDB的异或结果，并存储在Sv1中
            strXorUnit.strXor(Sv1, Sv1, sDB, HASH_LEN + 4);
            System.out.println("SV1的内容是:" + Arrays.toString(Sv1));
            // 使用F1Unit计算sk
            F1Unit.F1(sk, W, DataUnit.WORD_LEN, v2, ckey, 2);
            System.out.println("sk的内容是:" + Arrays.toString(sk));
            //存放结果
            Arrays.fill(IH, (byte) 0);



            /*
            生成索引密文对(Ia, Iv)，其中Ia是索引地址，Iv是加密后的索引值。这些索引密文将被存储到Redis中，
            用于后续的检索操作。通过使用随机字符串、AES加密、哈希函数等技术，可以保护索引的机密性和完整性。
            每次循环处理一个文件，生成对应的索引密文对，最终生成DB[0]个索引密文对，
            覆盖了关键字wi对应的所有文件。这样，即使攻击者获取了索引数据，也无法直接了解原始的索引信息。

             */
            // 循环处理DB[0]次，生成索引密文(Iawi,vl,j, Ivwi,vl,j)
            for (int j = 0; j < DB[0]; j++) {   //DB[0]表示关键字wi对应的文件数量。

                //生成新的索引
                randomStrUnit.randomStr(sIndex, HASH_LEN);   //sIndex的大小本身是320,对sIndex的前32位重新随机生成A
                System.out.println("sIndex的内容是:" + Arrays.toString(sIndex));
                // 使用AES_EUnit计算EI
                EI = AES_EUnit.AES_E(sIndex, sk, HASH_LEN);  //将sIndex前面32位数值进行加密获得EI

                //获得index后面位数的数值
                sI = resetUnit.reset288(sIndex,HASH_LEN);  //获得索引没有加密的后面位数
                //转化成字符串进行存储
                StoreUnit.LR.put(Base64.getEncoder().encodeToString(EI),Base64.getEncoder().encodeToString(sI));

                System.out.println("EI的内容是:" + Arrays.toString(EI));
                System.out.println("EI转换成字符串:" + Base64.getEncoder().encodeToString(EI));
                System.out.println();
                // 使用addHashUnit计算IH和EI的哈希值，并存储在IH中
                addHashUnit.addHash(IH, IH, EI, HASH_LEN);
                // 使用h34Unit计算Ia
                h34Unit.h34(Ia, st2, HASH_LEN, j, 3);
                // 使用h34Unit计算Iv
                h34Unit.h34(Iv, st2, HASH_LEN, j, 4);
                // 使用strXorUnit计算Iv和EI的异或结果，并存储在Iv中
                strXorUnit.strXor(Iv, Iv, EI, HASH_LEN);
                // 将Ia和Iv编码后存储到Redis中
                stringRedisTemplate.opsForValue().set(Base64.getEncoder().encodeToString(Ia), Base64.getEncoder().encodeToString(Iv));
                System.out.println("Ia的内容是:" + Arrays.toString(Ia));
                System.out.println("Ia的加密内容是:" +Base64.getEncoder().encodeToString(Ia));
                System.out.println("Iv的内容是:" + Arrays.toString(Iv));
                System.out.println("IV的加密内容是:" + Base64.getEncoder().encodeToString(Iv));
            }

            // 将IH复制到hd中
            System.arraycopy(IH, 0, hd, 0, HASH_LEN);
            // 使用hkeyUnit计算Pv2，生成证明(Pawi,vl, Pvwi,vl)
            hkeyUnit.hkey(Pv2, hd, Pv1, HASH_LEN, st2, HASH_LEN);
            // 将Pa2和Pv2编码后存储到Redis中
            stringRedisTemplate.opsForValue().set(Base64.getEncoder().encodeToString(Pa2), Base64.getEncoder().encodeToString(Pv2));
            System.out.println("Pa2的内容是:" + Arrays.toString(Pa2));
            System.out.println("Pa2的加密内容是:" + Base64.getEncoder().encodeToString(Pa2));
            System.out.println("Pv2的内容是：" + Arrays.toString(Pv2));
            System.out.println("Pv2的加密内容是:" + Base64.getEncoder().encodeToString(Pv2));
            // 将Sa1和Sv1编码后存储到Redis中
            stringRedisTemplate.opsForValue().set(Base64.getEncoder().encodeToString(Sa1), Base64.getEncoder().encodeToString(Sv1));
            System.out.println("Sa1的内容是:" + Arrays.toString(Sa1));
            System.out.println("Sa1的加密内容是:" + Base64.getEncoder().encodeToString(Sa1));
            System.out.println("Sv1的内容是:" + Arrays.toString(Sv1));
            System.out.println("Sv1的加密内容是:" + Base64.getEncoder().encodeToString(Sv1));

            //将pv进行还原
            resetUnit.reset(Pv2,DataUnit.HASH_LEN);
        }
        // 将v2编码后存储到Redis中
        stringRedisTemplate.opsForValue().set(String.valueOf(l), Base64.getEncoder().encodeToString(v2));
        System.out.println("l的具体数值为："+String.valueOf(l));
        System.out.println("v2的加密内容是:" + Base64.getEncoder().encodeToString(v2));
        // 将v2复制到stl中
        System.arraycopy(v2, 0, stl, 0, HASH_LEN);
        // 返回版本号l
        return l;
    }

}
